﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections;

namespace SocketLibs
{
	// State object for reading client data asynchronously
	public class StateObject
	{
		// Client  socket.
		public Socket workSocket = null;
		// Size of receive buffer.
		public const int BufferSize = 1024;
		// Receive buffer.
		public byte[] buffer = new byte[BufferSize];
		// Received data string.
		public StringBuilder sb = new StringBuilder();
	}

	public class SocketListener
	{
		// Clients as Hashtable
		private Hashtable Clients;
		private int Port;
		private bool DoJobs = true;

		// Server socket
		private Socket _host;
		protected Socket Host
		{
			get
			{
				if (this._host == null)
				{
					// Establish the local endpoint for the socket.
					// The DNS name of the computer
					// running the host is "host.contoso.com".
					IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
					IPAddress ipAddress = ipHostInfo.AddressList[0];
					IPEndPoint localEndPoint = new IPEndPoint(ipAddress, this.Port);

					// Create a TCP/IP socket.
					_host = new Socket(AddressFamily.InterNetwork,
						SocketType.Stream, ProtocolType.Tcp);

					// Bind the socket to the local endpoint.
					_host.Bind(localEndPoint);
				}
				return this._host;
			}
		}

		// Thread signal.
		public ManualResetEvent t = new ManualResetEvent(false);

		public SocketListener(int portNumber)
		{
			// Instantiates client collection container
			this.Clients = new Hashtable();
			this.Port = portNumber;
		}

		public void StopHost() 
		{
			//Host.Shutdown(SocketShutdown.Both);
			Host.Close();
			DoJobs = false;
		}

		public void StartListening()
		{
			// Listen for incoming connections.
			try
			{
				Host.Listen(100);
				
				while (DoJobs)
				{
					// Set the event to nonsignaled state.
					t.Reset();

					// Start an asynchronous socket to listen for connections.
					Host.BeginAccept(
						new AsyncCallback(AcceptCallback),
						Host);

					// Wait until a connection is made before continuing.
					t.WaitOne();
				}

			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
			}

		}

		public void AcceptCallback(IAsyncResult ar)
		{
			// Signal the main thread to continue.
			t.Set();

			// Get the socket that handles the client request.
			Socket host = (Socket)ar.AsyncState;

			if (host == null) return;

			try
			{
				Socket client = host.EndAccept(ar);

				// Create the state object.
				StateObject state = new StateObject();
				state.workSocket = client;
				client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
					new AsyncCallback(ReadCallback), state);
			}
			catch (ObjectDisposedException e)
			{
				// Do nothing, this block comes closed
			}
		}

		public void ReadCallback(IAsyncResult ar)
		{
			String content = String.Empty;

			// Retrieve the state object and the client socket
			// from the asynchronous state object.
			StateObject state = (StateObject)ar.AsyncState;
			Socket client = state.workSocket;

			// Read data from the client socket. 
			int bytesRead = client.EndReceive(ar);

			if (bytesRead > 0)
			{
				// There  might be more data, so store the data received so far.
				state.sb.Append(Encoding.ASCII.GetString(
					state.buffer, 0, bytesRead));

				// Check for end-of-file tag. If it is not there, read 
				// more data.
				content = state.sb.ToString();
				if (content.IndexOf("<EOF>") > -1)
				{
					// All the data has been read from the 
					WhatsMessage(content);
				}
				else
				{
					// Not all data received. Get more.
					client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
					new AsyncCallback(ReadCallback), state);
				}
			}
		}

		private void Send(Socket handler, String data)
		{
			// Convert the string data to byte data using ASCII encoding.
			byte[] byteData = Encoding.ASCII.GetBytes(data);

			// Begin sending the data to the remote device.
			handler.BeginSend(byteData, 0, byteData.Length, 0,
				new AsyncCallback(SendCallback), handler);
		}

		private void SendCallback(IAsyncResult ar)
		{
			try
			{
				// Retrieve the socket from the state object.
				Socket handler = (Socket)ar.AsyncState;

				// Complete sending the data to the remote device.
				int bytesSent = handler.EndSend(ar);
				Console.WriteLine("Sent {0} bytes to client.", bytesSent);

				handler.Shutdown(SocketShutdown.Both);
				handler.Close();

			}
			catch (Exception e)
			{
				Console.WriteLine(e.ToString());
			}
		}

		private void WhatsMessage(string content)
		{
			
		}

	}
}